#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>

typedef struct {
    char login[21];
    char mode[21];
    char sex;
} info;

typedef struct {
    char login[21];
    unsigned int offset;
} pIndex;

typedef struct {
    char pIndex[15][21];
    char mode[21];
} mIndex;

typedef struct {
    char pIndex[50][21];
    char sex;
} sIndex;

void searchMode(char *mode, pIndex *pindex, mIndex *mindex, FILE *fpData, int pIndexTam, int mIndexTam)
{
    int i, j, k, tam, count = 0, cnt = 0;
    info infoTmp;

    //demand by mode in the index
    while((strcmp(mode, mindex[cnt].mode) > 0) && (cnt < mIndexTam - 1))
        cnt++;
    if (!strcmp(mode, mindex[cnt].mode)){
        //counts how many entries there are in that mode
        for (i = 0; i < 15; i++)
            if (mindex[cnt].pIndex[i][0] != '\0')
                count++;

        printf("%d\n", count);
        for (i = 0; i < count; i++){
            j = 0;
            while ((strcmp (mindex[cnt].pIndex[i], pindex[j].login) > 0) && (j < pIndexTam - 1))
                j++;
            if (!strcmp (pindex[j].login, mindex[cnt].pIndex[i])){
                fseek(fpData, pindex[j].offset, SEEK_SET);
                fgetc(fpData);
                fgetc(fpData);
                tam  = strlen(pindex[j].login);
                for (k = 0; k < tam; k++)
                    infoTmp.login[k] = fgetc(fpData);
                infoTmp.login[tam] = '\0';
                fgetc(fpData);
                tam  = strlen(mindex[cnt].mode);
                for (k = 0; k < tam; k++)
                    infoTmp.mode[k] = fgetc(fpData);
                infoTmp.mode[tam] = '\0';
                fgetc(fpData);
                infoTmp.sex = fgetc(fpData);
                printf("%s %s %c\n", infoTmp.login, infoTmp.mode, infoTmp.sex);
            }
        }
    }
    else printf("%d\n", count);
}

void searchSex(char sex, pIndex *pindex, sIndex *sindex, FILE *fpData, int pIndexTam, int sIndexTam)
{
    int i, j, k, tamT, tam, count = 0, cnt = 0;
    info infoTmp;

    //demand by sex in the index
    while((sex > sindex[cnt].sex) && (cnt < sIndexTam - 1))
        cnt++;
    if(sex == sindex[cnt].sex){
        //counts how many entries there are in that sex
        for (i = 0; i < 50; i++)
            if (sindex[cnt].pIndex[i][0] != '\0')
                count++;

        printf("%d\n", count);
        for (i = 0; i < count; i++){
            j = 0;
            while ((strcmp (sindex[cnt].pIndex[i], pindex[j].login) > 0) && (j < pIndexTam - 1))
                j++;
            if (!strcmp (pindex[j].login, sindex[cnt].pIndex[i])){
                fseek(fpData, pindex[j].offset, SEEK_SET);
                fscanf(fpData, "%d", &tamT);
                tam  = strlen(pindex[j].login);
                for (k = 0; k < tam; k++)
                    infoTmp.login[k] = fgetc(fpData);
                infoTmp.login[tam] = '\0';
                fgetc(fpData);
                tamT = tamT - tam - 6;
                for (k = 0; k < tamT; k++)
                    infoTmp.mode[k] = fgetc(fpData);
                infoTmp.mode[tamT] = '\0';
                fgetc(fpData);
                infoTmp.sex = fgetc(fpData);
                printf("%s %s %c\n", infoTmp.login, infoTmp.mode, infoTmp.sex);
            }
        }
    }
    else printf("%d\n", count);
}

void searchDual(char *mode, char sex, pIndex *pindex, mIndex *mindex, sIndex *sindex, FILE *fpData, int pIndexTam, int mIndexTam, int sIndexTam)
{
    int i, j, k = 0, tam, countM = 0, countS = 0, countD = 0, cntM = 0, cntS = 0;
    info infoTmp;
    pIndex *indexTmp;

    //demand by mode in the index
    while((strcmp(mode, mindex[cntM].mode) > 0) && (cntM < mIndexTam - 1))
        cntM++;

    //demand by sex in the index
    while((sex > sindex[cntS].sex) && (cntS < sIndexTam - 1))
        cntS++;

    if ((!strcmp(mode, mindex[cntM].mode)) && (sex == sindex[cntS].sex)){
        //counts how many entries there are in that mode
        for (i = 0; i < 15; i++)
            if (mindex[cntM].pIndex[i][0] != '\0')
                countM++;

        //counts how many entries there are in that sex
        for (i = 0; i < 50; i++)
            if (sindex[cntS].pIndex[i][0] != '\0')
                countS++;

        //counts how many entries there are in that mode and sex
        for (i = 0; i < countM; i++)
            for (j = 0; j < countS; j++)
                if ((!strcmp(mindex[cntM].pIndex[i], sindex[cntS].pIndex[j])) && (mindex[cntM].pIndex[i][0] != '\0'))
                    countD++;

        printf("%d\n", countD);

        //match
        i = 0;
        j = 0;
        indexTmp = (pIndex*) malloc (countD * sizeof(pIndex));
        while ((k < countD) && (i < countM) && (j < countS)){
            if (strcmp(mindex[cntM].pIndex[i], sindex[cntS].pIndex[j]) > 0)
                j++;
            else if (strcmp(sindex[cntS].pIndex[j], mindex[cntM].pIndex[i]) > 0)
                i++;
            else {
                for (tam = 0; tam < (int)strlen(mindex[cntM].pIndex[i]); tam++)
                    indexTmp[k].login[tam] = mindex[cntM].pIndex[i][tam];
                indexTmp[k].login[(int)strlen(mindex[cntM].pIndex[i])] = '\0';
                i++;
                j++;
                k++;
            }
        }

        for (i = 0; i < countD; i++){
            j = 0;
            while ((strcmp(indexTmp[i].login, pindex[j].login) > 0) && (j < pIndexTam - 1))
                j++;
            if (!strcmp(indexTmp[i].login, pindex[j].login)){
                fseek(fpData, pindex[j].offset, SEEK_SET);
                fgetc(fpData);
                fgetc(fpData);
                tam = strlen (pindex[j].login);
                for (k = 0; k < tam; k++)
                    infoTmp.login[k] = fgetc(fpData);
                infoTmp.login[tam] = '\0';
                fgetc(fpData);
                tam = strlen(mindex[cntM].mode);
                for (k = 0; k < tam; k++)
                    infoTmp.mode[k] = fgetc(fpData);
                infoTmp.mode[tam] = '\0';
                fgetc(fpData);
                infoTmp.sex = fgetc(fpData);
                printf("%s %s %c\n", infoTmp.login, infoTmp.mode, infoTmp.sex);
            }
        }
    }
    else printf("%d\n", countD);
}

//function that calculates the number of records in a file
int calcRec (FILE *file)
{
    fseek (file , 0 , SEEK_END);
    int size = ftell (file);
    rewind (file);
    return size;
}

int main()
{
    int pIndexTam, mIndexTam, sIndexTam, flag = 1;
    info entry;
    pIndex *pindex;
    mIndex *mindex;
    sIndex *sindex;
    char instr[3];
    FILE *fpData, *fpIndex, *fpIndex1, *fpIndex2;

    char client[48];
    unsigned int offset;
    int length, count, i, j, k, cnt;

    /*try to open the files, if any of them returns false and the program closes*/
    if((fpData = fopen("data.dat", "a+")) == NULL) return -1;
    if((fpIndex = fopen("index.dat", "a+b")) == NULL) return -1;
    if((fpIndex1 = fopen("index1.dat", "a+b")) == NULL) return -1;
    if((fpIndex2 = fopen("index2.dat", "a+b")) == NULL) return -1;

    //calculates the record length
    pIndexTam = calcRec(fpIndex);
    mIndexTam = calcRec(fpIndex1);
    sIndexTam = calcRec(fpIndex2);

    //allocates space
    pindex = (pIndex*) calloc (pIndexTam, sizeof(pIndex));
    mindex = (mIndex*) calloc (mIndexTam, sizeof(mIndex));
    sindex = (sIndex*) calloc (sIndexTam, sizeof(sIndex));

    //loads into main memory index files
    fread(pindex, sizeof(pIndex), pIndexTam, fpIndex);
    fread(mindex, sizeof(mIndex), mIndexTam, fpIndex1);
    fread(sindex, sizeof(sIndex), sIndexTam, fpIndex2);

    while(flag)
    {
        scanf("%s",instr);
        if(!strcmp(instr,"IC"))
        {
            cnt = 0;

            scanf("%s %s %c", entry.login, entry.mode, &entry.sex);
            length = strlen(entry.login) + strlen(entry.mode) + 1/*sex*/ + 3/*delimeters*/ + 2/*number of characters*/;

            //operations on the file
            sprintf(client, "%d%s|%s|%c|", length, entry.login, entry.mode, entry.sex);
            fseek (fpData, 0, SEEK_END);
            offset = ftell(fpData);
            fprintf(fpData,"%s", client);

            //increases the primary index
            pIndexTam++;
            pindex = (pIndex*) realloc (pindex, pIndexTam * sizeof(pIndex));
            k = pIndexTam - 1;
            pindex[k].login[0] = '\0';
            //find position
            if(pIndexTam > 1){
                while ((strcmp(entry.login, pindex[cnt].login) > 0) && (cnt < pIndexTam - 1))
                    cnt++;
                //shift the registers
                for (i = (pIndexTam - 1); i > cnt; i--)
                    pindex[i] = pindex[i-1];
            }

            //put the login and the offset on primary index
            for (i = 0; i < (int)strlen(entry.login); i++)
                pindex[cnt].login[i] = entry.login[i];
            pindex[cnt].login[(int)strlen(entry.login)] = '\0';
            pindex[cnt].offset = offset;

            //begin mode index
            count = 0;
            i = 0;
            //case is the first register
            if (mIndexTam == 0){
                mIndexTam++;
                mindex = (mIndex*) realloc (mindex, mIndexTam * sizeof(mIndex));
                for (i = 0; i < (int)strlen(entry.mode); i++)
                    mindex[0].mode[i] = entry.mode[i];
                mindex[0].mode[(int)strlen(entry.mode)] = '\0';
                for (i = 0; i < (int)strlen(entry.login); i++)
                    mindex[0].pIndex[0][i] = entry.login[i];
                mindex[0].pIndex[0][(int)strlen(entry.login)] = '\0';
                for (i = 1; i < 15; i++)
                    mindex[0].pIndex[i][0] = '\0';
            }

            else {
                //find position mode
                i = 0;
                while ((strcmp(entry.mode, mindex[count].mode) > 0) && (count < mIndexTam))
                    count++;
                //the mode already exists
                if (!strcmp(entry.mode, mindex[count].mode)){
                    //find position login
                    while ((strcmp(entry.login, mindex[count].pIndex[i]) > 0) && (i < 14)){
                        if (mindex[count].pIndex[i][0] == '\0')
                            break;
                        i++;
                    }
                    //shift the registers
                    for (j = 14; j > i; j--)
                        for (k = 0; k < 21; k++)
                            mindex[count].pIndex[j][k] = mindex[count].pIndex[j-1][k];
                    //copy the login
                    for (j = 0; j < (int)strlen(entry.login); j++)
                        mindex[count].pIndex[i][j] = entry.login[j];
                    mindex[count].pIndex[i][(int)strlen(entry.login)] = '\0';
                }
                //the mode doesnt exists
                else {
                    mIndexTam++;
                    mindex = (mIndex*) realloc (mindex, mIndexTam * sizeof(mIndex));
                    //shift the registers
                    for (i = (mIndexTam - 1); i > count; i--)
                        mindex[i] = mindex[i-1];
                    //copy the mode
                    for (i = 0; i < (int)strlen(entry.mode); i++)
                        mindex[count].mode[i] = entry.mode[i];
                    mindex[count].mode[(int)strlen(entry.mode)] = '\0';

                    //copy the login
                    for (i = 0; i < (int)strlen(entry.login); i++)
                        mindex[count].pIndex[0][i] = entry.login[i];
                    mindex[count].pIndex[0][(int)strlen(entry.login)] = '\0';

                    for (i = 1; i < 15; i++)
                        mindex[count].pIndex[i][0] = '\0';
                }
            }
            //end mode index

            //begin sex index
            count = 0;
            i = 0;
            //case is the first register
            if (sIndexTam == 0){
                sIndexTam++;
                sindex = (sIndex*) realloc (sindex, sIndexTam * sizeof(sIndex));
                sindex[0].sex = entry.sex;
                for (i = 0; i < (int)strlen(entry.login); i++)
                    sindex[0].pIndex[0][i] = entry.login[i];
                for (i = 1; i < 50; i++)
                    sindex[0].pIndex[i][0] = '\0';
            }

            else {
                //find position sex
                i = 0;
                while ((entry.sex > sindex[count].sex) && (count < sIndexTam))
                    count++;
                //the sex already exists
                if (entry.sex == sindex[count].sex){
                    //find position login
                    while ((strcmp(entry.login, sindex[count].pIndex[i]) > 0) && (i < 49)){
                        if (sindex[count].pIndex[i][0] == '\0')
                            break;
                        i++;
                    }
                    //shift the registers
                    for (j = 49; j > i; j--)
                        for (k = 0; k < 21; k++)
                            sindex[count].pIndex[j][k] = sindex[count].pIndex[j-1][k];
                    //copy the login
                    for (j = 0; j < (int)strlen(entry.login); j++)
                        sindex[count].pIndex[i][j] = entry.login[j];
                    sindex[count].pIndex[i][(int)strlen(entry.login)] = '\0';
                }
                //the sex doesnt exists
                else {
                    sIndexTam++;
                    sindex = (sIndex*) realloc (sindex, sIndexTam * sizeof(sIndex));
                    //shift the registers
                    for (i = (sIndexTam - 1); i > count; i--)
                        sindex[i] = sindex[i-1];
                    //copy the sex
                    sindex[count].sex = entry.sex;
                    //copy the login
                    for (i = 0; i < (int)strlen(entry.login); i++)
                        sindex[count].pIndex[0][i] = entry.login[i];
                    sindex[count].pIndex[0][(int)strlen(entry.login)] = '\0';

                    for (i = 1; i < 50; i++)
                        sindex[count].pIndex[i][0] = '\0';
                }
            }
            //end sex index
        }
        else if(!strcmp(instr,"BM"))
        {
            scanf("%s", entry.mode);
            searchMode(entry.mode, pindex, mindex, fpData, pIndexTam, mIndexTam);
        }
        else if(!strcmp(instr,"BD"))
        {
            scanf("%s %c", entry.mode, &entry.sex);
            searchDual(entry.mode, entry.sex, pindex, mindex, sindex, fpData, pIndexTam, mIndexTam, sIndexTam);
        }
        else if(!strcmp(instr,"BS"))
        {
            scanf(" %c", &entry.sex);
            searchSex(entry.sex, pindex, sindex, fpData, pIndexTam, sIndexTam);
        }
        else if(!strcmp(instr,"FM"))
        {
            fclose(fpData);
            //save index files
            fwrite(pindex, sizeof(pIndex), pIndexTam, fpIndex);
            fwrite(mindex, sizeof(mIndex), mIndexTam, fpIndex1);
            fwrite(sindex, sizeof(sIndex), sIndexTam, fpIndex2);
            fclose(fpIndex);
            fclose(fpIndex1);
            fclose(fpIndex2);
            flag = 0;
        }
    }

    fpData = fopen("data.dat", "w");
    fpIndex = fopen("index.dat", "wb");
    fpIndex1 = fopen("index1.dat", "wb");
    fpIndex2 = fopen("index2.dat", "wb");
    fclose(fpData);
    fclose(fpIndex);
    fclose(fpIndex1);
    fclose(fpIndex2);

    return 0;
}
